
local json = require("cjson")

-- GLOBAL VARIABLES
-- & meant to be assigned in config.lua
--------------------
-- location of downloaded images
pleroma_avatar_save_path = nil
-- prepended to image file, written on page
pleroma_avatar_path = nil 

-- instances may require auth to access api
pleroma_auth = {}
-- pleroma_auth["instance.tld"] = "your_auth_key"

pcall(
    function () 
        dofile("config.lua")
    end
)

--[[

if `pleroma_avatar_*` are still `nil` after `dofile()` then the script will look for it in the yaml header.

if they are still nil afterwards, then  the script
will hotlink.

]]--

-- for testing 
function printTable(t)
    if t then
        for key, value in pairs(t) do
            print(key, value)
        end
    end
end

function add_unique(list, item)
    if type(list) ~= "table" or item == nil then
--         print("item "..item)
        return false
    end
    for _, value in ipairs(list) do 
        if value == item then
            return false
        end
    end
    table.insert(list, item)
--     print("added "..item)
    return true
end

function tokenizeString(inputString, delimiter)
    local tokens = {}
    for token in inputString:gmatch("[^" .. delimiter .. "]+") do
        table.insert(tokens, token)
    end
    return tokens
end

function get(link, filename, auth)
    print("http/s GET: ".. link)

    local filename = filename or nil
    local auth = auth or nil

    local args = {}
    if filename then -- when requesting avatars
        args = {
            "--timeout=10",
            "-qO",
            filename,
            link
        }
    else -- when requesting json
        args = {
            "-qO-",
            "--timeout=10",
            link
        }
    end
    -- don't use auth bearer for downloading images'
    -- its either not needed OR
    -- there should be an extra check on the host
    if auth and not filename then
        local h = "--header=\"Authorization: Bearer %s\" " 
        table.insert(
            args, 1, string.format(h, auth)
        )
    end

    local command = "wget " .. table.concat(args, ' ')
    print(command)
    local success, retval = pcall(
        function () 
            local handle = io.popen(command)
            local result = handle:read("*a")
            handle:close()
            return result

            -- couldn't get pandoc.pipe() send headers'
--             return pandoc.pipe("wget", args, "") 
        end
    )
    if not success then
        print("warning: error while performing http/s GET")
        print("\treturned: " .. tostring(retval))
    end
    return retval
end

function get_epoch_time(timestamp)
    local pattern = "(%d+)-(%d+)-(%d+)T(%d+):(%d+):(%d+).000Z"
    local year, month, day, hour, min, sec = timestamp:match(pattern)
    local epoch = os.time({
        year = year,
        month = month,
        day = day,
        hour = hour,
        min = min,
        sec = sec
    })
    return epoch
end

function get_short_date(timestamp)
    return os.date(
        "%a, %B %d, %Y", get_epoch_time(timestamp)
    )
end

function write_comments(pleroma_posts, instance, avatar_path)
    local avatar_mode = 1
    if avatar_path then
        avatar_mode = 2
    end

    -- img mode : 0 = omit; 1 = display (hotlink); 2 = download
    function get_user(acct_data, instance, img_mode, folder)
        -- specify path to store avatars
        -- should be empty string if img_mode != 2
        local folder = folder or ""

        -- related to output
        local user_info = "" -- template
        local result = ""

        local vars = {
            alias = acct_data["display_name"],
            uid = acct_data["id"],
            handle = acct_data["acct"],
            host=instance
        }
        local filename = nil
        local avatar_url = acct_data["avatar_static"]
        if img_mode then 
            user_info = [[
            <figure>
                <img src="$avatar$" loading="lazy" alt="avatar"/>
                <figcaption>$alias$ <a href="$host$/users/$uid$">@$handle$</a> </figcaption>
            </figure>
            ]]
            if img_mode == 1 then 
                vars.avatar = acct_data["avatar_static"]
            else
--                 save to file such as user_at_instance_tld.png
                -- omit query e.g ?name="foo" - get the extension only
                local extension = (avatar_url:match("([^?]+)")):match("^.+(%..+)$")
                -- replace '@'s and '.'
                local name = (acct_data["acct"]:gsub("@", "_at_")):gsub("%.", "_")
                filename = name .. extension
                vars.avatar = folder .. filename
            end
            result = user_info:gsub("%$(%w+)%$", vars)
        else
            user_info = "<p>$a`lias$ <a href=\"$host$/users/$uid$\">@$handle$</a></p>"
            result = user_info:gsub("%$(%w+)%$", vars)
        end
--         print("vars: " .. vars.avatar)
        return result, filename, avatar_url
    end

    function get_card(card, instance)
        if card == nil or type(card) ~= "table" then
            return ""
        end
        if card["provider_url"] == instance then
            -- skip rendering a card
            return ""
        end
--         print(type(card))
        local card_template = [[
    <article class="card">
        <header>
            <h1 class="card-title">$title$</h1>
            <p class="card-description">$description$</p>
        </header>
<!--        <img src="$image$" alt="$image_description$" class="card-image" loading="lazy"/> -->
        <footer>
            <a href="$link$" class="card-link">Read More</a>
        </footer>
    </article>
        ]]
        local vars = {
            title = card["title"],
            description = card["description"],
            image = card["image"],
            image_description=card["image_description"],
            link = card["url"]
        }
        return card_template:gsub("%$(%w+)%$", vars)
    end

    function get_media(attachments)
        if type(attachments) ~= "table" then
            return ""
        end
        if #attachments < 1 then
            return ""
        end
        local media_list = {"<p>media attached: </p><ol>"}
        local item = "<li><a href=\"$link$\">$mime$</a></li>"
        for _, v in pairs(attachments) do
            local vars = {
                link = v["preview_url"],
                mime = v["pleroma"]["mime_type"]
            }
            local foo = item:gsub("%$(%w+)%$", vars)
--             print(foo)
            table.insert(media_list, foo)
        end
        table.insert(media_list, "</ol>")
        return table.concat(media_list, "\n")
    end

    function get_poll(poll)
        if type(poll) ~= "table" then
            return ""
        end
        local bar_chart = {"<div class=\"chart\">"}
        local bar_template = [[
            <div class="bar-container">
                <div class="bar" style="width: $pct$%;">
                    <span>$pct$%</span>
                </div>
                <div class="bar-text">
                    $label$
                </div>
            </div>
        ]]
        local total_votes = math.floor(poll["votes_count"])
        local total_voters = math.floor(poll["voters_count"])
        for _, v in pairs(poll["options"]) do
            local percentage = (v["votes_count"]/total_votes) * 100
            local rounded = math.floor(0.5 + percentage)
            local vars = {
                label = v["title"],
                pct = rounded
            }
            local bar = bar_template:gsub("%$(%w+)%$", vars)
            table.insert(bar_chart, bar)
        end
        -- close chart div
        table.insert(bar_chart, "</div>")

        local summary = "<p>$x$ people have cast $y$ votes</p>"
        local foo = summary:gsub(
            "%$(%w+)%$", 
            {x=total_voters, y=total_votes}
        ) 
        table.insert(bar_chart, foo)
        return table.concat(bar_chart,"\n")
    end

    if #pleroma_posts == 0  then
        return ""
    end
    local template = [[
<article class="pleroma-comment" id="pleroma-comment$i$">
    <h3>
        #$i$ <a href="$host$/notice/$pid$">$datetime$</a>
    </h3>
    $user$
    <blockquote>
        $text$
    </blockquote>
    $card$
    $attributes$
</article>
]]
    local comments = {}
    local replies = pleroma_posts-- ["descendants"]

    local links = {}
    local images = {}

    for i, post in ipairs(replies) do
        local pid = post["id"]
        local datetime = get_short_date(post["created_at"])
        local text = post["content"]

        local attrs = {}
        table.insert(
            attrs, get_media(post["media_attachments"])
        )
        table.insert(attrs, get_poll(post["poll"]))

        local user, img_file, img_url = get_user(
            post["account"], instance, avatar_mode, avatar_path)
        add_unique(images, img_file)
        add_unique(links, img_url)

        local interpolated = template:gsub("%$(%w+)%$", {
            i= #replies - i + 1,
            host=instance,
            pid=pid,
            datetime=datetime,
            user=user,
            text = text,
            card = get_card(post["card"], instance),
            attributes = table.concat(attrs)
        })
--         print(interpolated)
        table.insert(
            comments, pandoc.RawBlock("html", interpolated)
        )
    end
--     printTable(dl_list)
    return comments, images, links
end

function combine_tables(a,b)
    -- iterate through b, add to a
    for i=1,#b do
        table.insert(a, b[i])
    end
    return a
end

function get_url_from_pandoc_str(pandoc_str) 
    local str = pandoc.utils.stringify(pandoc_str)
    -- 1 = protocol, 2 = host ...
    -- https://host.tld/notice/12345
    local tokens = tokenizeString(str, '/')
    local id = tokens[#tokens]

    local host = tokens[2]
    local id   = tokens[#tokens]
    local link = str

    return link, host, id
end

function get_status(host, post_id, auth)
    local url = "https://" .. host .. "/api/v1/statuses/" .. post_id
    local success, retval =  pcall(
        function ()
            local got = get(url, nil, auth)
--             print(got)
            return json.decode(got)
        end
    )
    -- if an error occurred in retrieving a status
    -- it will cause errors in write_comments
     --consider skipping over statuses later
    assert(success)
    assert(not retval["error"])
    return retval
end

function get_replies(host, id, auth)
    local url = "https://" .. host ..  "/api/v1/statuses/" .. id .. "/context"
--     print(url)
    local got = json.decode(get(url, nil, auth))
    return got["descendants"]
end

function get_images(filenames, urls, folder)
    if not folder then
        folder = ""
    end
    if not filenames or not urls then
        return
    end
    if #filenames ~= #urls then
        return
    end
    for i = 1, #urls, 1 do 
        -- still possible to have a ilst of nil (file names)
        if not filenames[i]  then
            break
        end
        get(urls[i], folder .. filenames[i])
    end
end

function Meta(meta)
    local pleroma_urls = meta["pleroma-urls"]
    if pleroma_urls == nil then
        return -- abort
    end

    -- if both are defined, then do not hotlink avatars
    if not pleroma_avatar_save_path then
        pleroma_avatar_save_path = meta["pleroma-avatar-save-path"]
    end
    if not pleroma_avatar_path then
        pleroma_avatar_path = meta["pleroma-avatar-path"]
    end
    -- most servers appear to serve hotilnked avatars however
    -- images will be missing in case of downtime or shutdown
    -- OR a user has changed their avatar and the old avatar gets deleted

    -- var currently unused
--     local is_hotlink = true 
--     if pleroma_avatar_save_path and pleroma_avatar_path then
--         is_hotlink = false
--     end
    local all_replies = {}
    local hrefs = {}
    local host = ""
    -- for each listed url in "pleroma-urls"
    for _, v in pairs(pleroma_urls) do
        local link, domain, id = get_url_from_pandoc_str(v)
        host = domain

        -- list of links people can reply using
        local reply_href = link
        if type(pleroma_reply_href) == "string" then 
            local temp = "https://%s%s%s"
            reply_href = string.format(
                temp, host, pleroma_reply_href, id)
        end
        table.insert(hrefs, 
            {link = reply_href, id = id}
        )

        local auth_key = nil
        if pleroma_auth[host] then
            auth_key = pleroma_auth[host]
        end
        local op = get_status(host, id, auth_key)
        table.insert(all_replies, op)
        local replies = get_replies(host, id, auth_key)
        combine_tables(all_replies, replies)
    end
    table.sort(all_replies, 
        function(a, b) 
            local ta = get_epoch_time(a["created_at"])
            local tb = get_epoch_time(b["created_at"])
            return ta > tb
        end
    )
    -- returns comments, images, links (img urls)
    local c, i, l = write_comments(
        all_replies, 
        "https://" .. host,
        pleroma_avatar_path
    )
    get_images(i, l, pleroma_avatar_save_path)
    meta["pleroma-comments"] = c
    meta["pleroma-comments-count"] = #c
    meta["pleroma-has-comments"] = (#c > 0)
    meta["pleroma"] = hrefs
    return meta
end
